package com.maniu.h264maniu;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

public class H265Player implements Runnable {
//    数据源

    //    解码器
    MediaCodec mediaCodec;
    private String path;
    private byte[] bytes = null;
    //surface    ffmpeg
    //    显示   目的   surface
    private Surface surface;

    public H265Player(String path, Surface surface) {
        this.path = path;
        this.surface = surface;
//具体编码名称  h264  h265  vp8 vp9      编码的历史  编码   ITU-T  ISO  名字
//        ISO  的   正规的  mpeg4-avc    h264 编码格式 是有限的
//        初始化

        try {
            //MediaFormat.MIMETYPE_VIDEO_AVC = "video/avc"
            mediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
//
            MediaFormat mediaformat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC,
                    1920, 960);
            mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
//            自己的参数  才是 交给dsp
            mediaCodec.configure(mediaformat, surface, null, 0);
        } catch (IOException e) {
            e.printStackTrace();
        }
//        当前支持  硬解H264
        Log.i("david", "支持: ");
    }

    public H265Player(byte[] data, Surface surface) {
        this.bytes = data;
        this.surface = surface;
//具体编码名称  h264  h265  vp8 vp9      编码的历史  编码   ITU-T  ISO  名字
//        ISO  的   正规的  mpeg4-avc    h264 编码格式 是有限的
//        初始化

        try {
            //MediaFormat.MIMETYPE_VIDEO_AVC = "video/avc"
            mediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
//368x384 可以随便设置
            MediaFormat mediaformat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC,
                    1920, 960);
            mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
//            自己的参数  才是 交给dsp
            mediaCodec.configure(mediaformat, surface, null, 0);
        } catch (IOException e) {
            e.printStackTrace();
        }
//        当前支持  硬解H264
        Log.i("david", "支持: ");
    }

    public void play() {
        mediaCodec.start();
        new Thread(this).start();
    }

    @Override
    public void run() {
//        解码   休息10min
//        这个宽高设置小了会不会有问题，如果解析sps真实数据宽高远远大于设置的宽高

        try {
            decodeH264();
        } catch (Exception e) {
            Log.i("david", "run: " + e.toString());
        }
    }

    private void decodeH264() {
//        byte[] bytes = null;
//        try {
////            bytes 数组里面
//            bytes = getBytes(path);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        int startIndex = 0;
        boolean first = false;
        long startWhen = 0;
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        boolean end = false;
        while (!end) {
            // startIndex不加数字 start==end  start 不会移动
            // startIndex + 1 会和 0x00 0x00 0x01 冲突 还是会 不移动
            // startIndex + 2 比较合适
            // startIndex + n  n越大丢失的像素越多
            int nextFrameStart = findByFrame(bytes, startIndex + 2, bytes.length);
            //        ByteBuffer[] byteBuffers= mediaCodec.getInputBuffers();
            int inIndex = mediaCodec.dequeueInputBuffer(10000);
//            8K
            if (inIndex >= 0) {
                ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inIndex);
//            到底丢多少      文件大小 99M  3M崩了
//            次数 帧来丢   一帧的内容
//                byte
                if(nextFrameStart == -1){
                    mediaCodec.queueInputBuffer(
                            inIndex,
                            0,
                            0,
                            0,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM
                    );
                }else{
                    int length = nextFrameStart - startIndex;
                    byteBuffer.clear();
                    Log.i("david", "decodeH264: 输入  " + length + ", nextFrameStart:" + nextFrameStart + ", startIndex:" + startIndex);
//                丢一部分   数据 到容器 length
                    byteBuffer.put(bytes, startIndex, length);
//dsp   bytebuffer   dsp 1        dsp   索引   容器 2
                    mediaCodec.queueInputBuffer(inIndex, 0, length, System.currentTimeMillis(), 0);
                    startIndex = nextFrameStart;
                }

            }
//            解码了  索引     索引 已经 3秒   dsp  数据 //            8K 是1  不是
//            大于1   等于 小于2
            int outIndex = mediaCodec.dequeueOutputBuffer(info, 10000);
            if (outIndex >= 0) {
                switch (outIndex) {
                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                        Log.d("H265Player", outIndex + " format changed");
                        break;
                    case MediaCodec.INFO_TRY_AGAIN_LATER:
                        Log.d("H265Player", outIndex + " 解码当前帧超时");
                        break;
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        //outputBuffers = videoCodec.getOutputBuffers();
                        Log.d("H265Player", outIndex + " output buffers changed");
                        break;
                    default:
                        if (!first) {
                            startWhen = System.currentTimeMillis();
                            first = true;
                        }
                        //解析帧的速度比较快
                        //防止画面放的太快
                        try {
                            Log.d("H265Player", outIndex + " startWhen:"+startWhen + ",presentationTimeUs:"+info.presentationTimeUs/1000);
//                            long sleepTime =
//                                    (info.presentationTimeUs / 1000) - (System.currentTimeMillis() - startWhen);
//                            if (sleepTime > 0) Thread.sleep(sleepTime);
                            Thread.sleep(40);
//                            Log.d("H265Player", outIndex + " sleepTime:"+sleepTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        mediaCodec.releaseOutputBuffer(outIndex, true);
                        break;
                }

            }
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                Log.d("H265Player", "buffer stream end");
                end = true;
            }
        }
        mediaCodec.stop();
        mediaCodec.release();
    }

    private int findByFrame(byte[] bytes, int start, int totalSize) {
        for (int i = start; i <= totalSize - 4; i++) {
            if (((bytes[i] == 0x00) && (bytes[i + 1] == 0x00) && (bytes[i + 2] == 0x00) && (bytes[i + 3] == 0x01))
                    || ((bytes[i] == 0x00) && (bytes[i + 1] == 0x00) && (bytes[i + 2] == 0x01))) {
                return i;
            }
        }
        return -1;
    }

    public byte[] getBytes(String path) throws IOException {
        InputStream is = new DataInputStream(new FileInputStream(new File(path)));
        int len;
        int size = 1024;
        byte[] buf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        buf = new byte[size];
        while ((len = is.read(buf, 0, size)) != -1)
            bos.write(buf, 0, len);
        buf = bos.toByteArray();
        return buf;
    }
}
